<?php

declare(strict_types=1);

namespace app\middleware;

use think\Request;
use think\Response;
use think\exception\HttpException;
use app\service\CryptoService;
use app\service\UserService;

/**
 * 加密验证中间件
 * 处理请求加密解密和用户身份验证
 */
class EncryptionMiddleware
{
    // 排除不需要加密的路由
    protected $encryptRoutes = [
        'api/v1/comment/agreement',
        'api/v1/user/uploadAvatar',
        'api/v1/comment/area',
        'api/v1/product/mini_qrcode',
        'api/v1/comment/banner',
        'api/v1/comment/gridList',
        'api/v1/product/hotRecommend',
        'api/v1/coupon/getCouponList',
        'api/v1/product/search',
        'api/v1/travel/scenicSpots/getSpotList'
    ];

    // 修改成排除验证的路由
    protected $authRoutes = [
        'api/v1/user/login',
        'api/v1/comment/agreement',
        'api/v1/user/uploadAvatar',
        'api/v1/comment/area',
        'api/v1/travel/scenicSpots/detail',
        'api/v1/product/mini_qrcode',
        'api/v1/comment/banner',
        'api/v1/comment/gridList',
        'api/v1/product/hotRecommend',
        'api/v1/coupon/getCouponList',
        'api/v1/product/search',
        'api/v1/travel/scenicSpots/getSpotList'
    ];

    // 排除Token验证的路由（但仍可能需要加密）
    // protected $excludeRoutes = [
    //     'v1/user_login/save',
    //     'v1/user_login/register',
    //     'v1/index/index',
    //     'v1/health/check'
    // ];

    /**
     * 处理请求
     *
     * @param Request $request
     * @param \Closure $next
     * @return Response
     */
    public function handle(Request $request, \Closure $next)
    {
        $route = $this->getCurrentRoute($request);
        try {
            // 1. 处理加密请求
            // if ($this->needEncryption($route)) {
            //     $this->handleEncryptedRequest($request);
            // }

            // 2. 验证用户身份

            $this->handleAuthentication($request, $route);

            // 3. 执行下一个中间件或控制器
            $response = $next($request);

            // 4. 处理响应加密
            // if ($this->needEncryption($route)) {
            //     return $this->handleEncryptedResponse($response);
            // }

            return $response;
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage(), 402);
        }
    }

    /**
     * 获取当前路由
     */
    protected function getCurrentRoute(Request $request): string
    {
        $pathinfo = $request->pathinfo();
        return trim($pathinfo, '/');
    }

    /**
     * 判断是否需要加密
     */
    protected function needEncryption(string $route): bool
    {
        // 排除不需要加密的路由
        if (empty($this->encryptRoutes)) {
            return true;
        }
        return !in_array($route, $this->encryptRoutes);
    }

    /**
     * 判断是否需要身份验证
     */
    protected function needAuth(string $route): bool
    {
        // 为空时，默认需要验证
        if (empty($this->authRoutes)) {
            return true;
        }
        // 修改成排除验证的路由
        return !in_array($route, $this->authRoutes);
    }

    /**
     * 处理加密请求
     */
    protected function handleEncryptedRequest(Request $request): void
    {
        // 检查是否为加密请求
        if (!$request->header('X-Encrypted') || $request->header('X-Encrypted') !== 'true') {
            throw new HttpException(405, '此接口需要加密请求');
        }

        // 获取请求数据
        $input = $request->all();
        if (empty($input)) {
            $input = json_decode($request->getContent(), true);
        }

        if (!$input || !isset($input['data'], $input['timestamp'], $input['nonce'], $input['sign'])) {
            throw new HttpException(400, '加密请求格式错误');
        }

        // 解密请求数据
        $cryptoService = new CryptoService();
        $decryptedData = $cryptoService->decryptRequest($input);

        // 将解密后的数据合并到请求参数中
        if (is_array($decryptedData)) {
            // 将解密的数据合并到现有请求参数中
            $existingParams = $request->param();
            $mergedParams = array_merge($existingParams, $decryptedData);
            // 通过反射设置请求参数
            $reflection = new \ReflectionClass($request);
            if ($reflection->hasProperty('param')) {
                $property = $reflection->getProperty('param');
                $property->setAccessible(true);
                $property->setValue($request, $mergedParams);
            }
        }
    }

    /**
     * 处理用户身份验证
     */
    protected function handleAuthentication(Request $request, string $route): void
    {
        $token = $this->getTokenFromRequest($request);

        if (!$token) {
            if ($this->needAuth($route)) {
                throw new HttpException(401, '缺少认证信息');
            }
        }

        if ($token) {
            $userService = new UserService();
            $user = $userService->verifyToken($token);
            if (!$user) {
                throw new HttpException(401, 'Token无效或已过期');
            }
            // 将用户信息设置到全局上下文
            UserService::setCurrentUser($user);
        }
    }

    /**
     * 从请求中获取Token
     */
    protected function getTokenFromRequest(Request $request): ?string
    {
        $authorization = $request->header('Authorization');

        if ($authorization && preg_match('/Bearer\s+(\S+)/', $authorization, $matches)) {
            return $matches[1];
        }

        return null;
    }

    /**
     * 处理加密响应
     */
    protected function handleEncryptedResponse(Response $response): Response
    {
        $content = $response->getContent();
        $data = json_decode($content, true);

        if (!$data) {
            return $response;
        }

        try {
            // $cryptoService = new CryptoService();
            // $encryptedData = $cryptoService->encryptResponse($data);

            return Response::create($data, 'json')
                ->header([
                    'X-Encrypted' => 'true',
                    'Content-Type' => 'application/json'
                ]);
        } catch (\Exception $e) {
            // 加密失败时返回原响应
            return $response;
        }
    }

    /**
     * 错误响应
     */
    protected function errorResponse(string $message, int $code = 400): Response
    {
        $data = [
            'code' => $code,
            'msg' => $message,
            'data' => null
        ];

        return json($data);
    }
}
